/*
 * mem_main.h
 *
 *  Created on: Jun 22, 2010
 *      Author: japoovey
 */

#ifndef MEM_MAIN_H_
#define MEM_MAIN_H_

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "uarch/mreq.h"
#include "uarch/module.h"
#include "uarch/stat_engine.h"
#include "uarch/settings.h"

using namespace std;

#define START_ADDR		0x1b57cd98fe

#define	ALT_MASK 		0xffffffffffffffff
#define MASK 			0x0000000fffffffff

#define ADDR_SEED 		0xabdf3000bf
#define ADDR_SEED2		0x1b57cd98fe
#define RAND_SEED1		0xfecbc30054
#define RAND_SEED2		0x9c00cf4912
#define TRACE_LENGTH 	1000000
#define PAGE_SIZE_OS		12

typedef struct {
    char valid;
    unsigned long long int tag;
} tag_t;

typedef struct {
    char valid;
    unsigned long long int lru;
    unsigned long long int tag;
} fa_tag_t;


class Memory_controller : public Module
{
public:
	Memory_controller(ModuleID moduleID, int hit_time);
	~Memory_controller();

	timestamp_t process_request (unsigned long long addr);

	void run_trace();
	void print_config(FILE * file);


	void tick();
	void tock();

    Memory_controller_stat_engine *stats;

private:
    inline void preprocess_request (Mreq *request);

    void update_address_history (unsigned long long addr);

	unsigned long long generate_addr(unsigned long long old_addr, unsigned long long access_num);

	inline unsigned long long get_dram_page_level_tag(unsigned long long curr_addr);
	inline unsigned long long get_dram_page_level_set_index(unsigned long long curr_addr);
	inline unsigned long long get_dram_page_level_assoc_index(unsigned long long curr_addr);

	inline unsigned long long get_dram_line_level_tag(unsigned long long curr_addr);
	inline unsigned long long get_dram_line_level_set_index(unsigned long long curr_addr);
	inline unsigned long long get_dram_line_level_assoc_index(unsigned long long curr_addr);

	inline unsigned long long get_pcm_page_level_tag(unsigned long long curr_addr);
	inline unsigned long long get_pcm_page_level_set_index(unsigned long long curr_addr);
	inline unsigned long long get_pcm_page_level_assoc_index(unsigned long long curr_addr);

	inline unsigned long long get_pcm_row_buffer_tag(unsigned long long curr_addr);

	unsigned long long get_pcm_rb_lru_index();

	tag_t **pcm_tag;
    tag_t **dram_tag;
   	tag_t **dram_line_tag;
   	fa_tag_t *pcm_cache;

	unsigned long long current_addr;
	unsigned long long prev_addr[2];

	// Used when model is disabled
	int hit_time;
};

class Memory_controller_stat_engine : public Stat_engine
{
public:
    Memory_controller_stat_engine (Module *m);
    Memory_controller_stat_engine ();
    ~Memory_controller_stat_engine ();

    void global_stat_merge (Stat_engine * e);
    void clear_stats();
	void print_stats (ostream & out);

	void start_warmup ();
	void end_warmup ();
    void save_samples ();

    /** Stats **/

    /** Off-chip accesses.  */
  	/** Output Stats */
    Persistent_stat<counter_t> dmcounter;
    Persistent_stat<counter_t> dlcounter;
    Persistent_stat<counter_t> pmcounter;
    Persistent_stat<counter_t> plcounter;
    Persistent_stat<counter_t> hdcounter;
    Persistent_stat<double> total_latency;
    Persistent_stat<counter_t> off_chip_accesses;
    Persistent_stat<counter_t> off_chip_SUPPLY_S_accesses;
    Persistent_stat<counter_t> off_chip_SUPPLY_E_accesses;
    Persistent_stat<counter_t> off_chip_SUPPLY_M_accesses;
    Persistent_stat<counter_t> off_chip_GETIS_accesses;
    Persistent_stat<counter_t> off_chip_GETIM_accesses;
    Persistent_stat<counter_t> off_chip_PUTM_accesses;

    Persistent_histogram_stat<counter_t> read_latency_hist;
};

#endif /* MEM_MAIN_H_ */
